using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Web.UI;
using System.IO;
using System.Xml;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;

namespace ProjectCompliance.WebParts
{

    #region STRUCTS & ENUMS

    /// <summary>
    /// Data type containing all information specifying a compliance rule
    /// </summary>
    public struct ComplianceRule
    {
        public string RuleName;
        public ComplianceRuleOperator Operator;
        public string ContentTypeName;
        public SPContentTypeId ContentTypeId;
        public string LibraryName;
        public Guid LibraryGuid;
        public string ManualInstructions;
        public string Comments;
        public string FieldName;
        public ComplianceFieldType FieldType;
        public ComplianceFieldComparer FieldComparer;
        public object FieldValue;
        public Guid FieldGuid;

        internal int RuleId;
        internal ComplianceRules Ruleset;

        public void SetOperator(string operatorName)
        {
            if (operatorName != "")
            {
                switch (operatorName.ToLower())
                {
                    case "contenttypeexists":
                        Operator = ComplianceRuleOperator.ContentTypeExists;
                        break;
                    case "contenttypeexistsinlibrary":
                        Operator = ComplianceRuleOperator.ContentTypeExistsInLibrary;
                        break;
                    case "contenttypefieldhasvalue":
                        Operator = ComplianceRuleOperator.ContentTypeFieldHasValue;
                        break;
                    case "manual":
                        Operator = ComplianceRuleOperator.Manual;
                        break;
                    default:
                        throw new ArgumentException("Operator name invalid.");
                }
            }
        }

        public void SetFieldType(string fieldTypeName)
        {
            if (fieldTypeName != "")
            {
                switch (fieldTypeName.ToLower())
                {
                    case "text":
                        FieldType = ComplianceFieldType.Text;
                        break;
                    case "number":
                        FieldType = ComplianceFieldType.Number;
                        break;
                    case "datetime":
                        FieldType = ComplianceFieldType.DateTime;
                        break;
                    default:
                        throw new ArgumentException("Field Type name invalid.");
                }
            }
        }

        public void SetFieldComparer(string fieldComparer)
        {
            if (fieldComparer != "")
            {
                switch (fieldComparer.ToLower())
                {
                    case "equal":
                        FieldComparer = ComplianceFieldComparer.Equal;
                        break;
                    case "notequal":
                        FieldComparer = ComplianceFieldComparer.NotEqual;
                        break;
                    case "fieldgreater":
                        FieldComparer = ComplianceFieldComparer.FieldGreater;
                        break;
                    case "fieldgreaterorequal":
                        FieldComparer = ComplianceFieldComparer.FieldGreaterOrEqual;
                        break;
                    case "fieldless":
                        FieldComparer = ComplianceFieldComparer.FieldLess;
                        break;
                    case "fieldlessorequal":
                        FieldComparer = ComplianceFieldComparer.FieldLessOrEqual;
                        break;
                    default:
                        throw new ArgumentException("Field Comparer name invalid.");
                }
            }
        }

        public void Update()
        {
            if (Ruleset != null)
            {
                Ruleset.UpdateRule(this);
            }
            else
            {
                throw new ApplicationException("Rule is not associated with a Ruleset.");
            }
        }
    }


    /// <summary>
    /// The different operations allowed for a compliance rule
    /// </summary>
    public enum ComplianceRuleOperator
    {
        ContentTypeExists,
        ContentTypeExistsInLibrary,
        ContentTypeFieldHasValue,
        Manual
    }


    /// <summary>
    /// Data type containing all information specifying a compliance result
    /// </summary>
    public struct ComplianceResult
    {
        public ComplianceResultDecision Decision;
        public string RuleName;
        public int AuditNumber;
        public string CompliantItemName;
        public string CompliantItemUrl;
        public string ManualInstructions;
        public string ItemComments;
        public string AuditComments;
        public DateTime timestamp;
        public string auditorname;
    }


    /// <summary>
    /// The different outcomes allowed for a compliance result
    /// </summary>
    public enum ComplianceResultDecision
    {
        Untested = 0,
        Compliant = 1,
        Noncompliant = 2,
        NotApplicable = 3,
        Unknown = 4
    }


    public enum ComplianceFieldComparer
    {
          Equal = 0,
          NotEqual = 1,
          FieldGreater = 2,
          FieldGreaterOrEqual = 3,
          FieldLess = 4,
          FieldLessOrEqual = 5
    }

    public enum ComplianceFieldType
    {
        Text,
        Number,
        DateTime
    }

    #endregion


    /// <summary>
    /// Collection of compliance libraries, loaded from SharePoint list
    /// </summary>
    public class ComplianceLibraries : ListDataTable
    {
        /// <summary>
        /// List of compliance libraries, loaded from SharePoint
        /// </summary>
        /// <param name="SiteContext">Context of current site</param>
        /// <param name="ListName">Name of compliance library list</param>
        /// <param name="RootWeb">Set true to get list from site collection, otherwise false to get list from site.</param>
        public ComplianceLibraries(SPContext SiteContext, string ListName, bool RootWeb)
            : base(SiteContext, ListName, RootWeb)
        {
            targetWeb = SiteContext.Web;
        }

        /// <summary>
        /// List of compliance libraries, loaded from SharePoint
        /// </summary>
        /// <param name="web">Web on which list of libraries resides</param>
        /// <param name="ListName">Name of compliance library list</param>
        public ComplianceLibraries(SPWeb web, string ListName)
            : base(web, ListName)
        {
            targetWeb = web;
        }

        /// <summary>
        /// List of compliance libraries, loaded from SharePoint
        /// </summary>
        /// <param name="SiteUrl">Url of site on which list of libraries resides</param>
        /// <param name="ListName">Name of compliance library list</param>
        public ComplianceLibraries(string SiteUrl, string ListName)
            : base(SiteUrl, ListName)
        {
            targetWeb = base.web;
        }


        #region FIELDS

        private SPWeb targetWeb;

        #endregion


        /// <summary>
        /// Retrieve next library name in the list.
        /// </summary>
        /// <returns>SharePoint list object of the next library specified in the Libraries list</returns>
        public SPList GetLibrary()
        {
            DataRow row;
            SPList list;
            string libraryName = "";

            try
            {
                row = base.GetItem();
                libraryName = Util.GetString(row, "Title");
                list = targetWeb.Lists[libraryName];
            }
            catch
            {
                throw new ApplicationException("Library &quot;" + libraryName + "&quot; not found.");
            }
            return list;

            // Get as URL
            // Find library by name given by "description" portion of URL field.
            // URL is not used to find the library

            //string libraryField, libraryUrl;
            //libraryField = Util.GetString(row, "URL");
            //if (Util.rexUrlField.Match(libraryField).Success)
            //{
            //    libraryUrl = Util.rexUrlField.Match(libraryField).Result("$1");
            //    libraryName = Util.rexUrlField.Match(libraryField).Result("$2");
            //    try
            //    {
            //        list = targetWeb.Lists[libraryName];
            //    }
            //    catch
            //    {
            //        throw new ApplicationException("Library &quot;" + libraryName + "&quot; not found.");
            //    }
            //    return list;
            //}
            //else
            //{
            //    throw new ApplicationException("Can't parse Library information (" + libraryField + ").  Make sure the library Description field is filled out with the name of the library.");
            //}
        }


        /// <summary>
        /// Append libraries to a StringBuilder for display (debugging)
        /// </summary>
        /// <param name="output">StringBuilder to which xml describing Libraries list will be appended</param>
        /// <param name="withSchema">Flag indicating whether schema should also be displayed</param>
        public void AppendLibraries(ref StringBuilder output, bool withSchema)
        {
            base.AppendItems(ref output, withSchema);
        }

    }


    /// <summary>
    /// Collection of compliance rules, loaded from SharePoint list
    /// </summary>
    public class ComplianceRules : ListDataTable
    {
        public ComplianceRules(SPContext SiteContext, string ListName)
            : base(SiteContext, ListName, false)
        { }

        public ComplianceRules(SPWeb web, string ListName)
            : base(web, ListName)
        { }

        public ComplianceRules(string SiteUrl, string ListName)
            : base(SiteUrl, ListName)
        { }

        /// <summary>
        /// Get the current rule in the list
        /// </summary>
        /// <returns></returns>
        public ComplianceRule GetRule()
        {
            ComplianceRule Rule;

            Rule = new ComplianceRule();
            Util.LoadRule(base.GetItem(), ref Rule);

            Rule.RuleId = curItem;
            Rule.Ruleset = this;

            return Rule;
        }


        /// <summary>
        /// Save values from a Rule to the ListDataTable
        /// </summary>
        /// <param name="Rule"></param>
        internal void UpdateRule(ComplianceRule Rule)
        {
            DataRow row;
            if (Rule.Ruleset != null) //This test means the RuleId was set properly
            {
                row = base.ListTable.Rows[Rule.RuleId];
                row["Title"] = Rule.RuleName;
                row["ComplianceRuleOperator"] = Rule.Operator.ToString();
                row["ComplianceManualInstructions"] = Rule.ManualInstructions;
                row["ComplianceItemComments"] = Rule.Comments;
                row["ComplianceContenttypeName"] = Rule.ContentTypeName;
                row["ComplianceLibraryName"] = Rule.LibraryName;
                row["ComplianceFieldName"] = Rule.FieldName;
                row["ComplianceFieldValue"] = Rule.FieldValue;
                row["ComplianceFieldComparer"] = Rule.FieldComparer.ToString();
                row["ComplianceFieldType"] = Rule.FieldType.ToString();
                row["ComplianceContenttypeId"] = Rule.ContentTypeId.ToString();
                row["ComplianceLibraryGUID"] = Rule.LibraryGuid;
                row["ComplianceFieldGUID"] = Rule.FieldGuid;
            }
            else
            {
                throw new ApplicationException("Can only update Rules that were created by GetRule().");
            }
        }


        /// <summary>
        /// Append rules to a StringBuilder for display (debugging)
        /// </summary>
        /// <param name="output">StringBuilder to which xml describing Rules list will be appended</param>
        /// <param name="withSchema">Flag indicating whether schema should also be displayed</param>
        public void AppendRules(ref StringBuilder output, bool withSchema)
        {
            base.AppendItems(ref output, withSchema);
        }

    }


    /// <summary>
    /// Collection of compliance results, for storing into SharePoint list
    /// </summary>
    public class ComplianceResults : ListDataTable
    {

        public ComplianceResults(SPContext SiteContext, string ListName)
            : base(SiteContext, ListName, false)
        { }

        public ComplianceResults(SPWeb web, string ListName)
            : base(web, ListName)
        { }

        public ComplianceResults(string SiteUrl, string ListName)
            : base(SiteUrl, ListName)
        { }

        private int _nextAuditNumber = 0;

        /// <summary>
        /// Audit number of results marked as the final audit
        /// </summary>
        public int FinalAuditNumber
        {
            get
            {
                return Util.GetFinalAuditNumber(base.web);
            }
        }

        /// <summary>
        /// Auditor's comment about the final audit
        /// </summary>
        public string FinalAuditComment
        {
            get
            {
                return Util.GetFinalAuditComment(base.web);
            }
        }


        /// <summary>
        /// Audit number of results marked as the initial audit
        /// </summary>
        public int InitialAuditNumber
        {
            get
            {
                return Util.GetInitialAuditNumber(base.web);
            }
        }

        /// <summary>
        /// Auditor's comments about the initial audit
        /// </summary>
        public string InitialAuditComment
        {
            get
            {
                return Util.GetInitialAuditComment(base.web);
            }
        }


        /// <summary>
        /// Status of project for auditing purposes (open, closed)
        /// </summary>
        public string ProjectStatus
        {
            get
            {
                return Util.GetProjectStatus(base.web);
            }
        }

        /// <summary>
        /// Auditor's comments about the project and/or its status
        /// </summary>
        public string ProjectStatusComment
        {
            get
            {
                return Util.GetProjectStatusComment(base.web);
            }
        }


        /// <summary>
        /// Get an Audit Number that is one higher than any existing audit number
        /// </summary>
        /// <returns></returns>
        public int NextAuditNumber
        {
            get
            {
                int rowAuditNumber, maxAuditNumber = 0;

                if (_nextAuditNumber == 0)
                {
                    if ((base.ListTable != null) && (base.ListTable.Rows != null))
                    {
                        foreach (DataRow row in base.ListTable.Rows)
                        {
                            rowAuditNumber = Util.GetInt(row, "ComplianceAuditNumber");
                            if (rowAuditNumber > maxAuditNumber)
                                maxAuditNumber = rowAuditNumber;
                        }
                    }

                    _nextAuditNumber = maxAuditNumber + 1;
                }

                return _nextAuditNumber;
            }
        }


        public bool ContainsAuditNumber(int AuditNumber)
        {
            int rowAuditNumber;

            if ((base.ListTable != null) && (base.ListTable.Rows != null))
            {
                foreach (DataRow row in base.ListTable.Rows)
                {
                    rowAuditNumber = Util.GetInt(row, "ComplianceAuditNumber");
                    if (rowAuditNumber == AuditNumber)
                        return true;
                }
            }

            return false;
        }



        /// <summary>
        /// Append results to a StringBuilder for display (debugging)
        /// </summary>
        /// <param name="output">StringBuilder to which xml describing Results list will be appended</param>
        /// <param name="withSchema">Flag indicating whether schema should also be displayed</param>
        public void AppendResults(ref StringBuilder output, bool withSchema)
        {
            base.AppendItems(ref output, withSchema);
        }


        /// <summary>
        /// Advance current item to next if it has the given audit number
        /// </summary>
        /// <param name="AuditNumber"></param>
        /// <returns></returns>
        public Boolean Next(int AuditNumber)
        {
            DataRow row;
            int rowAuditNumber;

            if (!base.disposed)
            {
                base.curItem++;
                while (base.curItem < base.numItems)
                {
                    row = base.GetItem();
                    rowAuditNumber = Util.GetInt(row, "ComplianceAuditNumber");
                    if (rowAuditNumber == AuditNumber)
                        return true;
                    else
                        base.curItem++;
                }
                return false;
            }
            else
            {
                throw new ApplicationException(disposedException);
            }
        }

        /// <summary>
        /// Get the current Result in the list
        /// </summary>
        /// <returns></returns>
        public ComplianceResult GetResult()
        {
            ComplianceResult Result;
            DataRow row;
            string strItemUrl, strDecision;

            Result = new ComplianceResult();
            row = base.GetItem();

            Result.RuleName = Util.GetString(row, "Title");
            Result.AuditNumber = Util.GetInt(row, "ComplianceAuditNumber");
            Result.CompliantItemName = Util.GetString(row, "ComplianceItemName");
            Result.ManualInstructions = Util.GetString(row, "ComplianceManualInstructions");
            Result.ItemComments = Util.GetString(row, "ComplianceItemComments");
            Result.AuditComments = Util.GetString(row, "ComplianceAuditComments");

            // Get ItemUrl; SharePoint formats as "url, description" so parse out url
            // and return that by itself as the URL; take the description as the name if
            // there's not already a name.
            strItemUrl = Util.GetString(row, "ComplianceItemUrl");
            if (Util.rexUrlField.Match(strItemUrl).Success)
            {
                Result.CompliantItemUrl = Util.rexUrlField.Match(strItemUrl).Result("$1");
                if (Result.CompliantItemName == "")
                    Result.CompliantItemName = Util.rexUrlField.Match(strItemUrl).Result("$2");
            }
            else
            {
                Result.CompliantItemUrl = strItemUrl;
            }

            // Get RuleResult as enumerated value, not the string that's stored
            strDecision = Util.GetString(row, "ComplianceRuleResult");
            switch (strDecision.ToLower())
            {
                case "untested":
                    Result.Decision = ComplianceResultDecision.Untested;
                    break;
                case "compliant":
                    Result.Decision = ComplianceResultDecision.Compliant;
                    break;
                case "noncompliant":
                    Result.Decision = ComplianceResultDecision.Noncompliant;
                    break;
                case "n/a":
                case "notapplicable":
                    Result.Decision = ComplianceResultDecision.NotApplicable;
                    break;
                default:
                    Result.Decision = ComplianceResultDecision.Unknown;
                    break;
            }
            // The following columns are created by SharePoint, so will not be found in SetResult
            Result.timestamp = Util.GetDateTime(row, "Created");
            Result.auditorname = Util.GetString(row, "Author");

            return Result;
        }



        /// <summary>
        /// Add a result to the results table
        /// </summary>
        /// <param name="Result">A result object, which contains the rule and the decision</param>
        public void SetResult(ComplianceResult Result)
        {
            DataRow row;

            row = this.ListTable.NewRow();
            row["Title"] = Result.RuleName;
            row["ComplianceRuleResult"] = Result.Decision.ToString();
            row["ComplianceAuditNumber"] = Result.AuditNumber;
            row["ComplianceItemName"] = Result.CompliantItemName;
            row["ComplianceItemUrl"] = Result.CompliantItemUrl;
            row["ComplianceManualInstructions"] = Result.ManualInstructions;
            row["ComplianceItemComments"] = Result.ItemComments;
            row["ComplianceAuditComments"] = Result.AuditComments;

            base.ListTable.Rows.Add(row);
        }


        /// <summary>
        /// Add a result to the results table
        /// </summary>
        /// <param name="Rule">Compliance rule that this result pertains to</param>
        /// <param name="Decision">The outcome of evaluating the compliance rule</param>
        /// <param name="CompliantItemName">Name of the item that resulted in the Decision</param>
        /// <param name="CompliantItemUrl">URL of the item that resulted in the Decision</param>
        public void SetResult(ComplianceRule Rule, ComplianceResultDecision Decision, string CompliantItemName, string CompliantItemUrl)
        {
            ComplianceResult Result;

            Result = new ComplianceResult();

            // Values from the rule
            Result.RuleName = Rule.RuleName;
            Result.ManualInstructions = Rule.ManualInstructions;

            // Values from parameters
            Result.Decision = Decision;
            Result.CompliantItemName = CompliantItemName;
            Result.CompliantItemUrl = CompliantItemUrl;

            // Following are not set from code - they are populated from ListView of Results 
            // Result.ItemComments = ItemComments;
            // Result.AuditComments = AuditComments;

            // Values from properties (current state of object)
            Result.AuditNumber = NextAuditNumber;

            SetResult(Result);
        }


        /// <summary>
        /// Store results from results table back into SharePoint list
        /// </summary>
        public void StoreResults()
        {
            base.Update();
        }

    }
}
